package nachos.threads;

import nachos.machine.*;
import java.util.LinkedList;
import java.util.ListIterator;

/**
 * Uses the hardware timer to provide preemption, and to allow threads to sleep
 * until a certain time.
 */
public class Alarm {
    /**
     * Allocate a new Alarm. Set the machine's timer interrupt handler to this
     * alarm's callback.
     *
     * <p><b>Note</b>: Nachos will not function correctly with more than one
     * alarm.
     */
    public Alarm() {
	Machine.timer().setInterruptHandler(new Runnable() {
		public void run() { timerInterrupt(); }
	    });
    }

    /**
     * The timer interrupt handler. This is called by the machine's timer
     * periodically (approximately every 500 clock ticks). Causes the current
     * thread to yield, forcing a context switch if there is another thread
     * that should be run.
     */
    public void timerInterrupt() {
    	checkTimer();
	KThread.currentThread().yield();
    }

    public void checkTimer(){
	//List iterating and checking
	boolean intStatus = Machine.interrupt().disable();

	ListIterator<Pair<KThread, Long>> iterator = waitQueue.listIterator();
	while(iterator.hasNext()){
		Pair<KThread, Long> pair = iterator.next();
		if(pair.getSecond() <= Machine.timer().getTime()){
			pair.getFirst().ready();
			iterator.remove();
		}
	}
	
	Machine.interrupt().restore(intStatus);
    }

    /**
     * Put the current thread to sleep for at least <i>x</i> ticks,
     * waking it up in the timer interrupt handler. The thread must be
     * woken up (placed in the scheduler ready set) during the first timer
     * interrupt where
     *
     * <p><blockquote>
     * (current time) >= (WaitUntil called time)+(x)
     * </blockquote>
     *
     * @param	x	the minimum number of clock ticks to wait.
     *
     * @see	nachos.machine.Timer#getTime()
     */
    public void waitUntil(long x) {
	// for now, cheat just to get something working (busy waiting is bad)
	boolean intStatus = Machine.interrupt().disable();	

	long wakeTime = Machine.timer().getTime() + x;	
	Pair<KThread, Long> pair = new Pair<KThread, Long>(KThread.currentThread(), wakeTime);
	waitQueue.add(pair);
	
	
	KThread.sleep();
	Machine.interrupt().restore(intStatus);
	
	//while (wakeTime > Machine.timer().getTime())
	//    KThread.yield();
    }

    LinkedList<Pair<KThread, Long>> waitQueue = new LinkedList<Pair<KThread, Long>>();
}
